package com.hqd.ch03.v51.context.support;

import com.hqd.ch03.utils.ObjectUtils;
import com.hqd.ch03.v51.beans.factory.config.DestructionAwareBeanPostProcessor;
import com.hqd.ch03.v51.beans.factory.support.MergedBeanDefinitionPostProcessor;
import com.hqd.ch03.v51.context.ApplicationListener;
import com.hqd.ch03.v51.context.ConfigurableApplicationContext;
import com.hqd.ch03.v51.factory.support.RootBeanDefinition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {

    private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class);

    private final transient ConfigurableApplicationContext applicationContext;

    private final transient Map<String, Boolean> singletonNames = new ConcurrentHashMap<>(256);


    public ApplicationListenerDetector(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }


    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (ApplicationListener.class.isAssignableFrom(beanType)) {
            this.singletonNames.put(beanName, beanDefinition.isSingleton());
        }
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof ApplicationListener) {
            // potentially not detected as a listener by getBeanNamesForType retrieval
            Boolean flag = this.singletonNames.get(beanName);
            if (Boolean.TRUE.equals(flag)) {
                // singleton bean (top-level or inner): register on the fly
                //   this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
            } else if (Boolean.FALSE.equals(flag)) {
                this.singletonNames.remove(beanName);
            }
        }
        return bean;
    }

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) {
    }

    @Override
    public boolean requiresDestruction(Object bean) {
        return (bean instanceof ApplicationListener);
    }


    @Override
    public boolean equals(Object other) {
        return (this == other || (other instanceof ApplicationListenerDetector &&
                this.applicationContext == ((ApplicationListenerDetector) other).applicationContext));
    }

    @Override
    public int hashCode() {
        return ObjectUtils.nullSafeHashCode(this.applicationContext);
    }

}
